package com.jzo2o.health.job;

import com.jzo2o.api.trade.RefundRecordApi;
import com.jzo2o.api.trade.dto.response.ExecutionResultResDTO;
import com.jzo2o.api.trade.enums.RefundStatusEnum;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.model.CurrentUserInfo;
import com.jzo2o.common.utils.CollUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.health.enums.OrderPayStatusEnum;
import com.jzo2o.health.enums.OrderStatusEnum;
import com.jzo2o.health.mapper.OrdersRefundMapper;
import com.jzo2o.health.model.domain.Orders;
import com.jzo2o.health.model.domain.OrdersCancelled;
import com.jzo2o.health.model.domain.OrdersRefund;
import com.jzo2o.health.service.IOrdersCancelledService;
import com.jzo2o.health.service.IOrdersRefundService;
import com.jzo2o.health.service.IOrdersService;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 订单管理微服务的定时任务
 *
 * @Author mr.wu
 * @Date 2024-12-11 21:39
 */
@Slf4j
@Component
public class OrderJob {

    @Autowired
    private IOrdersRefundService ordersRefundService;

    @Autowired
    private ApplicationContext applicationContext;

    @Resource
    private IOrdersCancelledService ordersCancelledService;

    /**
     * 定时自动退款
     */
    @XxlJob("handleRefundOrders")
    public void batchOrderRefund(){
        //获取当前类的代理对象
        OrderJob owner = applicationContext.getBean(OrderJob.class);

        //1.查询退款记录列表（每次查100条）
        List<OrdersRefund> ordersRefundList = ordersRefundService.lambdaQuery().last("limit 100").list();
        if(CollUtils.isEmpty(ordersRefundList)) {
            return;
        }

        //2.遍历退款记录列表，逐一进行退款处理
        for (OrdersRefund ordersRefund : ordersRefundList) {
            owner.ordersRefundHandle(ordersRefund);
        }
    }

    @Autowired
    private RefundRecordApi refundRecordApi;

    @Autowired
    private IOrdersService ordersService;

    /**
     * 退款处理
     * @param ordersRefund
     */
    @Transactional(rollbackFor = Exception.class)
    public void ordersRefundHandle(OrdersRefund ordersRefund) {
        //1.调用支付服务的Feign接口进行退款申请
        ExecutionResultResDTO executionResultResDTO = null;
        try {
            executionResultResDTO = refundRecordApi.refundTrading(ordersRefund.getTradingOrderNo(), ordersRefund.getRealPayAmount());
        } catch (Exception e) {
            log.error("[订单退款处理]退款申请接口调用失败，退款信息：{}，异常信息", ordersRefund, e);
            throw new BadRequestException("[订单退款处理]退款申请接口调用失败");
        }

        //2.从响应结果中获取退款处理状态
        Integer refundStatus = executionResultResDTO.getRefundStatus();

        //3.如果是退款中，则再查询一次退款结果
        if(ObjectUtils.equals(refundStatus, OrderPayStatusEnum.REFUNDING.getStatus())) {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //再发起一次退款请求，就是查询退款结果了
            executionResultResDTO = refundRecordApi.refundTrading(ordersRefund.getTradingOrderNo(), ordersRefund.getRealPayAmount());
            refundStatus = executionResultResDTO.getRefundStatus();
        }

        //4.更新订单表退款字段信息（退款状态、支付服务退款订单号、第三方退款订单号）
        boolean resultOrder = ordersService.lambdaUpdate()
                .eq(Orders::getId, ordersRefund.getId()) //更新条件
                .set(Orders::getPayStatus, refundStatus) //退款状态
                .set(Orders::getRefundNo, executionResultResDTO.getRefundNo()) //支付服务退款订单号
                .set(Orders::getRefundId, executionResultResDTO.getRefundId()) //第三方退款订单号
                .update();
        if(!resultOrder){
            log.error("[订单退款处理]订单表退款状态更新失败，退款信息：{}", ordersRefund);
            return;
        }

//        5.删除退款记录
        boolean resultRefund = ordersRefundService.removeById(ordersRefund.getId());
        if(!resultRefund){
            log.error("[订单退款处理]删除退款记录失败，退款信息：{}", ordersRefund);
        }

        log.info("[订单退款处理]退款处理成功，退款信息：{}，退款状态：{}", ordersRefund, refundStatus);
    }


    /**
     * 取消派单中订单
     *
     * @param orders
     * @param cancelReason
     */
    @Async("taskExecutor")
    @Transactional(rollbackFor = Exception.class)
    public void cancelRefundOrder(Orders orders, String cancelReason, CurrentUserInfo userInfo) {
        //1. 添加取消记录
        OrdersCancelled ordersCanceled = new OrdersCancelled();
        ordersCanceled.setId(orders.getId());//订单id
        ordersCanceled.setCancellerId(userInfo.getId()); //取消人id
        ordersCanceled.setCancellerType(userInfo.getUserType());//取消人类型
        ordersCanceled.setCancellerName(userInfo.getName());//取消人姓名
        ordersCanceled.setCancelReason(cancelReason);//取消原因
        ordersCanceled.setCancelTime(LocalDateTime.now());//取消时间
        boolean resultCancel = ordersCancelledService.save(ordersCanceled);
        if(!resultCancel) {
            throw new DBException("添加取消记录失败");
        }

        //2. 更新订单状态 update orders set order_status=?,refund_status=? where id=?
        boolean resultOrder = ordersService.lambdaUpdate()
                .eq(Orders::getId, orders.getId())
                .set(Orders::getOrderStatus, OrderStatusEnum.CLOSED.getStatus()) //已关闭
                .set(Orders::getPayStatus, RefundStatusEnum.SENDING.getCode()) //退款中
                .update();
        if(!resultOrder) {
            throw new DBException("更新订单失败");
        }

        //3. 添加退款记录
        OrdersRefund ordersRefund = new OrdersRefund();
        ordersRefund.setId(orders.getId());
        ordersRefund.setTradingOrderNo(orders.getTradingOrderNo());
        //ordersRefund.setRealPayAmount(orders.getRealPayAmount());
        ordersRefund.setRealPayAmount(BigDecimal.valueOf(0.01));//由于前面测试支付都是0.01，这里为方便演示也只能写0.01。 不要使用new BigDecimal(double)，这种会精度丢失
        boolean resultRefund = ordersRefundService.save(ordersRefund);
        if(!resultRefund) {
            throw new DBException("添加退款记录失败");
        }

        OrderJob owner = applicationContext.getBean(OrderJob.class);
        //4. 申请退款
        owner.ordersRefundHandle(ordersRefund);
    }
}